package servlet;

import java.io.*;
import java.io.ObjectInputStream.GetField;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
//import javax.servlet.http.HttpSession;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.w3c.dom.Node;

import java.util.Properties;
//import org.apache.commons.lang3.StringEscapeUtils;

import com.sun.mail.pop3.POP3Folder;

/**
 * Servlet implementation class EmailServlet
 */
@WebServlet(urlPatterns = { "/Email", "/email", "/EmailServlet", "/emailServlet" }, initParams = { @WebInitParam(name = "mode", value = "") })
public class EmailServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	final static String CACHED_EMAIL_FOLDER = "EmailCaching";
	final static String CACHED_EMAIL_FILE_EXTENTION = ".email";
	/**
	 * Default constructor.
	 */
	boolean isSendingMail;
	public EmailServlet() {
		// TODO Auto-generated constructor stub
		isSendingMail = false;
	}

	private boolean sendMail(String strSubject, String strAccount, String strTOList, String strCCList, String strBCCList, String strContent) {
		Properties props = new Properties();
		props.put("mail.smtp.host", "smtp.gmail.com");
		props.put("mail.smtp.socketFactory.port", "465");
		props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.port", "465");

		try {

			Session session = Session.getInstance(props, new javax.mail.Authenticator() {
				protected PasswordAuthentication getPasswordAuthentication() {
					return new PasswordAuthentication("buzzsystememail", "buzzsystemtest");
				}
			});

			MimeMessage message = new MimeMessage(session);
			message.setFrom(new InternetAddress("buzzsystememail@gmail.com"));
			
			String TOs = strTOList;
			if (TOs == null || TOs.trim() == "")
			{
				return false;
			}

			message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(TOs));

			String CCs = strCCList;
			if (CCs != null && CCs.trim() != "")
			{
				message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(CCs));
			}
			
			String BCCs = strBCCList;
			if (BCCs != null && BCCs.trim() != "")
			{
				message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(BCCs));
			}

			message.setSubject(strSubject == null ? " " : strSubject, "utf-8");

			Multipart multiPart = new MimeMultipart();

			BodyPart bodyPart = new MimeBodyPart();
			bodyPart.setContent(strContent == null ? " " : strContent, "text/html; charset=utf-8");
			multiPart.addBodyPart(bodyPart);
			
			message.setContent(multiPart);
			
			Transport.send(message);
			System.out.println("Done");
			return true;

		} catch (MessagingException e) {
			// throw new RuntimeException(e);
			System.out.println("Sending error!" + e.toString());
			return false;
		}
		
		finally
		{
			isSendingMail = false;
		}

	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		String strMode = request.getParameter("mode");
		if (strMode == null)
			return;
		response.setContentType("text/xml; charset=UTF-8");
		
		if (strMode.equalsIgnoreCase("getNew"))
		{
			if (isSendingMail == true)
			{
				PrintWriter out = response.getWriter();
				out.println("<EmailList>");
				return;
			}
			//
			String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
	
			Properties props = new Properties();
			props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
			props.setProperty("mail.pop3.socketFactory.fallback", "false");
			props.setProperty("mail.pop3.port", "995");
			props.setProperty("mail.pop3.socketFactory.port", "995");
			props.setProperty("mail.pop3.ssl", "true");
			props.setProperty("mail.pop3.host", "pop.gmail.com");
	
			//
			String str = "";
			Session session = Session.getDefaultInstance(props);
			try {
	
				Store store = session.getStore("pop3");
				store.connect("pop.googlemail.com", "buzzsystememail", "buzzsystemtest");
	
				Folder folder = store.getFolder("inbox");
				folder.open(Folder.READ_WRITE);
	
				Message[] msgs = folder.getMessages();
	
				// Use a suitable FetchProfile
				FetchProfile fp = new FetchProfile();
				fp.add(FetchProfile.Item.ENVELOPE);
				folder.fetch(msgs, fp);
	
				str = createXMLEmailString(msgs, folder);
				folder.close(true);
				store.close();	
			} 
			catch (javax.mail.MessagingException ex) {
				System.out.println(ex.toString());
			}
			catch (Exception ex)
			{
				ex.printStackTrace();
			}
			PrintWriter out = response.getWriter();
			out.println(str);
		}
		else if (strMode.equalsIgnoreCase("readCache"))
		{
			//TODO
			//need to check for login also
			
			
			String str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<EmailList>\n" + readCachedEmails("buzzsystememail") + "\n</EmailList>";
				
			PrintWriter out = response.getWriter();
			out.println(str);
		}
		else if (strMode.equalsIgnoreCase("markAsRead"))
		{
			String strId = request.getParameter("emailId");
			markAsRead("buzzsystememail", strId, true);
		}
		else if (strMode.equalsIgnoreCase("markAsUnread"))
		{
			String strId = request.getParameter("emailId");
			markAsRead("buzzsystememail", strId, false);
		}
		else if (strMode.equalsIgnoreCase("send"))
		{
			
			request.setCharacterEncoding("utf-8");
			String strAccount = "buzzsystememail";// request.getParameter("account");
			String strSubject = request.getParameter("subject");
			String strContent = request.getParameter("content");
			String strTOList= request.getParameter("TO");
			String strCCList= request.getParameter("CC");
			String strBCCList= request.getParameter("BCC");
			
			try 
			{
				strSubject = java.net.URLDecoder.decode(strSubject, "UTF-8");
				strSubject = new String(strSubject.getBytes("ISO-8859-1"), "UTF-8");
				
				strContent = java.net.URLDecoder.decode(strContent, "UTF-8");
				strContent = new String(strContent.getBytes("ISO-8859-1"), "UTF-8");
			}
			catch (Exception ex) 
			{
				
			}

			
			if (strCCList == null)
				strCCList = "";

			if (strBCCList == null)
				strBCCList = "";

			isSendingMail = true;
			sendMail(strSubject, strAccount, strTOList, strCCList, strBCCList, strContent);

			PrintWriter out = response.getWriter();
			out.write("<emailId/>");
		}
	
	}

	private String createXMLEmailString(Message[] msgs, Folder folder) {
		try {
			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.newDocument();
			Element root = doc.createElement("EmailList");// EmailList
			doc.appendChild(root);

			POP3Folder popFolder = (POP3Folder) folder;

			for (int i = 0; i < msgs.length; i++) {
				Element uniqueId = doc.createElement("UniqueID");
				String strId = popFolder.getUID(msgs[i]);
				if (checkForCachingExistence("buzzsystememail", strId) == true)
				{
					continue;
				}

				Element emailItem = doc.createElement("EmailItem");// EmailItem
				emailItem.setAttribute("WasRead", "false");
				root.appendChild(emailItem);
				
				Element subject = doc.createElement("Subject");// Subject
				subject.setTextContent(msgs[i].getSubject());
				emailItem.appendChild(subject);

				Element sentDate = doc.createElement("SentDate");
				sentDate.setTextContent(util.DateTimeUtil.getDateTimeString(msgs[i].getSentDate()));
				emailItem.appendChild(sentDate);

				Element from = doc.createElement("From");
				from.setTextContent(getFromListString(msgs[i]));
				emailItem.appendChild(from);

				Element cc = doc.createElement("CCList");
				cc.setTextContent(getCCListString(msgs[i]));
				emailItem.appendChild(cc);

				Element to = doc.createElement("ToList");
				to.setTextContent(getToListString(msgs[i]));
				emailItem.appendChild(to);

				uniqueId.setTextContent(strId);
				emailItem.appendChild(uniqueId);
				
				Element content = doc.createElement("EmailContent");
				
				try
				{
					Multipart parts = (Multipart) msgs[i].getContent();
					int iPartCount = parts.getCount();
					for (int j = 0; j < iPartCount; j++) {
						BodyPart p = parts.getBodyPart(j);
						Element eBodyPart = doc.createElement("EmailContentPart");// BodyPart
						String strContentType = p.getContentType();
						if (strContentType.indexOf("text/html") > -1) {
							
							int index = strContentType.indexOf("charset=");
							String charset = strContentType.substring(index + 8/*length of(charset=)*/);
							//System.out.println(charset);
							
							String strContent = p.getContent().toString();
	
	//						if (charset.equalsIgnoreCase("ISO-8859-1"))
	//						{
	//							strContent = new String(strContent.getBytes("UTF-8"), "UTF-8");
	//							System.out.println("changed to utf-8");
	//						}
							strContent = strContent.replaceAll("<.?html>|<.?body>", "");
							eBodyPart.setTextContent(strContent);
							eBodyPart.setAttribute("PartType", p.getContentType());
							content.appendChild(eBodyPart);
						} else {
							// TODO: get some things else
							// eBodyPart.setAttribute("type", p.getContentType());
							// eBodyPart.setTextContent(p.getContent().toString());
							// content.appendChild(eBodyPart);
						}
					}
				}
				catch (ClassCastException  ex)
				{
					System.out.println("Caching email error: " + ex.toString());
					continue;
				}
				emailItem.appendChild(content);
				prepareEmailFolders("buzzsystememail");
				writeXmlNodeToFile(emailItem, getEmailCachingFolder() + "/" + "buzzsystememail", strId + CACHED_EMAIL_FILE_EXTENTION);
			}

			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();
			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");

			
			// create string from xml tree
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(doc);
			trans.transform(source, result);
			
			String xmlString = sw.toString();
			return xmlString;

		} catch (Exception e) {
			System.out.println("Error in caching: " + e.toString());
			return "<EmailList value=\"Error\"/>";
		}
	}
	
	private boolean checkForCachingExistence(String strAccount, String strId)
	{
		File file = new File(getEmailCachingFolder() + "/" + strAccount + "/" + strId + CACHED_EMAIL_FILE_EXTENTION);
		return file.exists();
	}
	
	private void markAsRead(String strAcc, String strEmailId, boolean wasRead)
	{   
		try
		{
			File file = new File(getEmailCachingFolder() + "/" + strAcc + "/" + strEmailId + CACHED_EMAIL_FILE_EXTENTION);
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(file);
			
			Element root = doc.getDocumentElement();
			root.setAttribute("WasRead", wasRead ? "true" : "false");
			
			writeXmlNodeToFile(root, getEmailCachingFolder() + "/" + "buzzsystememail", strEmailId + CACHED_EMAIL_FILE_EXTENTION);
			
		}
		catch (Exception ex)
		{
			System.out.println("Cannot mark" + strEmailId);
		}
	}
	
	private String readCachedEmails(String strAccount) {
		//String xmlString = "";
		try 
		{
			StringBuilder builder = new StringBuilder();
			byte[] buffer = new byte[8096];
			int byteCount;
            
			File folder = new File(getEmailCachingFolder() + "/" + strAccount);
			File[] files = folder.listFiles();
		    for (int i = 0; i < files.length; i++) {
	            FileInputStream fin = new FileInputStream( files[i].getAbsolutePath());
	            boolean reachEndOfFile = false;
	            while (reachEndOfFile == false)
	            {
	            	byteCount = fin.read(buffer);
	            	if (byteCount > 0)
	            	{
	            		String xmlString = new String(buffer, 0, byteCount, "UTF-8").replaceAll("<\\?xml .*\\?>", "");
	            		builder.append(xmlString);
	            	}
	            	else
	            	{
	            		reachEndOfFile = true;
	            	}
	            }
	            fin.close();
		    }
			
			return builder.toString();
		} 
		catch (Exception e) 
		{
			return "<EmailList value=\"error\"/>";
		}
	}
	
	private void prepareEmailFolders(String strAccount)
	{
		String str = getEmailCachingFolder();
		File dir = new File(str);
		try
		{
			if (dir.mkdir() == true)
			{
				//
			}
			else
			{
				
			}
			dir = new File(str + "/" + strAccount);
			dir.mkdir();
		}
		catch (Exception ex)
		{
			System.out.println("Cannot create EmailCaching folder!");
		}
	}

	private String getCurrentDir()
	{
		String str = this.getServletContext().getRealPath("");
		return str;
	}
	
	private String getEmailCachingFolder()
	{
		return getCurrentDir() + "/" + CACHED_EMAIL_FOLDER;
	}
	
	private void writeXmlNodeToFile(Node node, String path, String strFileName){
		try
		{
			FileOutputStream writer = new FileOutputStream(path + "/" + strFileName);
		    TransformerFactory tf = TransformerFactory.newInstance();
		    // identity
		    Transformer t = tf.newTransformer();
		    t.setOutputProperty(OutputKeys.INDENT, "yes");
		    t.transform(new DOMSource(node), new StreamResult(writer));
		    writer.close();
		}
		catch (Exception ex)
		{
			System.out.println("Cannot write email to xml file at writeXml()");
		}
	}

	String getFromListString(Message msg) {
		String str = "";
		try {
			Address[] adds = msg.getFrom();
			for (int i = 0; i < adds.length; i++) {
				str += adds[i].toString();
			}
		} catch (Exception e) {

		}
		return str;
	}

	String getToListString(Message msg) {
		String str = "";
		try {
			Address[] adds = msg.getRecipients(Message.RecipientType.TO);
			for (int i = 0; i < adds.length; i++) {
				str += adds[i].toString();
			}
		} catch (Exception e) {

		}
		return str;
	}

	String getCCListString(Message msg) {
		String str = "";
		String separator = "";
		try {
			Address[] adds = msg.getRecipients(Message.RecipientType.CC);
			for (int i = 0; i < adds.length; i++) {
				str += separator + adds[i].toString();
				separator = ", ";
			}
		} catch (Exception e) {

		}
		return str;
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");

	}

}